package com.bzkj.document.utils.finishedProductin;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.bzkj.common.domain.NccApiResponse;
import com.bzkj.document.enums.ProcessingType;
import com.bzkj.document.exception.DocumentProcessException;
import com.bzkj.document.exception.WarehouseNotFoundException;
import com.bzkj.document.mapper.FinishedProductInMapper;
import com.bzkj.document.mapper.WmsWareHouseInventoryMapper;
import com.bzkj.document.utils.common.DataProcessUtils;
import com.bzkj.document.utils.common.InventoryManagerUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * @projectName: prt-wms
 * @package: com.bzkj.document.utils.finishedProductin
 * @className: FinishProductInUtils
 * @description: 产成品入库单工具类
 * @author: C1309
 * @createDate: 2025年01月05日
 * @version: v1.0
 */
@Component
public class FinishProductInUtils {
    @Resource
    @Qualifier("threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor executor;

    @Resource
    private FinishedProductInMapper finishedProductInMapper;

    @Resource
    private InventoryManagerUtils inventoryManagerUtils;

    @Resource
    private WmsWareHouseInventoryMapper wareHouseInventoryMapper;

    // ==========================================================
    // 模块名称: 产成品入库单解析模块
    // 功能描述: 解析产成品入库单相关的业务逻辑
    // ==========================================================

    /**
     * 解析NCC产成品入库单
     *
     * @param type           解析类型（0：查询，1：确认，2：修改）
     * @param nccApiResponse 接口返回数据
     * @return 单据查询结果
     */
    public List<Map<String, Object>> parsingFinishProductIns(NccApiResponse nccApiResponse, int type) {
        JSONArray dataArray = nccApiResponse.getData() == null ? new JSONArray() : nccApiResponse.getData();

        List<CompletableFuture<Map<String, Object>>> futures = dataArray.stream()
                .map(dataObject -> JSONObject.parseObject(JSONObject.toJSONString(dataObject))) // 显式转换为 JSONObject
                .map(dataObject -> CompletableFuture.supplyAsync(() -> processOrder(dataObject, type), executor)
                        .exceptionally(ex -> { // 捕获线程中的异常
                            System.err.println("处理订单时发生异常：" + ex.getMessage());
                            return null;
                        }))
                .collect(Collectors.toList());

        CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));

        try {
            allFutures.get(); // 等待所有线程完成
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException("处理订单时发生异常：" + e.getMessage());
        }

        return futures.stream()
                .map(CompletableFuture::join)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    /**
     * 处理订单数据
     *
     * @param dataObject 订单数据
     * @param type       解析类型（0：查询，1：确认，2：修改）
     * @return 处理后的订单数据
     */
    private Map<String, Object> processOrder(JSONObject dataObject, int type) {
        JSONObject finProdInHead = dataObject.getJSONObject("ic.FinProdInHeadVO");
        if (finProdInHead == null) {
            return null;
        }
        Map<String, Object> finProdInHeadMap = parsingFinProdInHeadVO(finProdInHead);
        String vbillcode = finProdInHeadMap.get("vbillcode").toString();
        String cgeneralhid = finProdInHeadMap.get("cgeneralhid").toString();
        Object cwarehouseid = finProdInHeadMap.get("cwarehouseid");
        String vtrantypecode = finProdInHeadMap.get("vtrantypecode").toString();

        // 如果单据已存在，跳过处理
        if (type == ProcessingType.SELECT_NCC_ORDER.ordinal() && finishedProductInMapper.getBillIsExist(vbillcode) > 0) {
            return null;
        }

        finProdInHeadMap.put("isConfirm", 0);

        // 解析订单行
        JSONArray finProdInBodyList = dataObject.getJSONArray("ic.FinProdInBodyVO");
        if (finProdInBodyList == null || finProdInBodyList.isEmpty()) {
            return null;
        }

        List<Map<String, Object>> details = parseFinProdInBodyList(finProdInBodyList, vbillcode, cwarehouseid, type, cgeneralhid, vtrantypecode);

        // 包装订单头和订单行信息
        Map<String, Object> billData = new HashMap<>(2);
        billData.put("header", finProdInHeadMap);
        billData.put("details", details);

        return billData;
    }


    /**
     * 解析订单行数据列表
     *
     * @param materialOutBodyList 订单行 JSON 数组
     * @param vbillcode           单据编号
     * @param cwarehouse          仓库信息
     * @param type                解析类型（0：查询，1：确认，2：修改）
     * @param cgeneralhid         表头主键
     * @param vtrantypecode       单据类型
     * @return 订单行解析结果列表
     */
    private List<Map<String, Object>> parseFinProdInBodyList(JSONArray materialOutBodyList, String vbillcode,
                                                             Object cwarehouse, int type, String cgeneralhid,
                                                             String vtrantypecode) {
        List<Map<String, Object>> result;
        List<Exception> exceptions = new ArrayList<>(); // 用于收集异常

        result = materialOutBodyList.stream()
                .map(JSONObject.class::cast)
                .map(finishedBody -> {
                    try {
                        Map<String, Object> finProdInBodyVO = parsingFinProdInBodyVO(finishedBody);
                        if (type == ProcessingType.CONFIRM_ORDER.ordinal()) {
                            processWarehouseInfo(finProdInBodyVO, cwarehouse);
                        }
                        finProdInBodyVO.put("vbillcode", vbillcode);
                        finProdInBodyVO.put("cwarehouseid", cwarehouse);
                        finProdInBodyVO.put("cgeneralhid", cgeneralhid);
                        finProdInBodyVO.put("vtrantypecode", vtrantypecode);
                        return finProdInBodyVO;
                    } catch (Exception e) {
                        exceptions.add(e); // 收集异常
                        return null; // 返回 null 表示该行处理失败
                    }
                })
                .filter(Objects::nonNull) // 过滤掉处理失败的行
                .collect(Collectors.toList());

        // 流处理完成后，统一处理异常
        if (!exceptions.isEmpty()) {
            StringBuilder errorMessage = new StringBuilder();
            for (Exception exception : exceptions) {
                errorMessage.append("<li>").append(exception.getMessage()).append("</li>");
            }
            throw new DocumentProcessException("<ul>" + errorMessage + "</ul>");
        }
        return result;
    }

    /**
     * 处理仓库信息
     *
     * @param finProdInBodyVO 订单行数据
     * @param cwarehouse      仓库信息
     */
    private void processWarehouseInfo(Map<String, Object> finProdInBodyVO, Object cwarehouse) {
        @SuppressWarnings("unchecked")
        Map<String, Object> cmaterialoid = (Map<String, Object>) finProdInBodyVO.get("cmaterialoid");
        Object crowno = finProdInBodyVO.get("crowno");
        String pkOrCode = DataProcessUtils.getPkOrCode(finProdInBodyVO, "cmaterialoid");
        String wareHousePk = DataProcessUtils.getPkOrCode((Map<String, Object>) cwarehouse);

        List<HashMap<String, String>> warehouseList = inventoryManagerUtils.selectNccWarehouse(pkOrCode, wareHousePk);

        if (warehouseList != null && !warehouseList.isEmpty()) {
            HashMap<String, String> hashMap = warehouseList.get(0);
            finProdInBodyVO.put("sto_id", hashMap.get("sto_id"));
            finProdInBodyVO.put("stu_class_id", hashMap.get("stu_class_id"));
        } else {
            String errorMsg = "<li>行号->" + crowno + "：物料名称：<b>" + cmaterialoid.get("name") +
                    "<b>" + "在WMS系统中无法分配对应仓库！</li>";
            throw new WarehouseNotFoundException(errorMsg);
        }
    }

    /**
     * 解析NCC产品入库单头数据
     *
     * @param finProdInHeadVO 产品入库单头数据
     * @return 解析结果
     */
    private HashMap<String, Object> parsingFinProdInHeadVO(JSONObject finProdInHeadVO) {
        HashMap<String, Object> finProdInHead = new HashMap<>(18);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //vbillcode（单据编号）
        DataProcessUtils.getStringField(finProdInHeadVO, "vbillcode", finProdInHead);


        // modifier(最后修改人)
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "modifier");

        // fbillflag(单据状态)
        DataProcessUtils.getStringField(finProdInHeadVO, "fbillflag", finProdInHead);

        // ntotalnum（总数量）
        DataProcessUtils.getFormattedDouble(finProdInHeadVO, "ntotalnum", finProdInHead);

        //cgeneralhid（产成品入库单表头主键产成品入库单表头主键）
        DataProcessUtils.getStringField(finProdInHeadVO, "cgeneralhid", finProdInHead);

        //ctrantypeid(出入库类型）
        DataProcessUtils.getStringField(finProdInHeadVO, "ctrantypeid", finProdInHead);

        //cdptid(生产部门）
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "cdptid");

        //dmakedate(制单日期）
        DataProcessUtils.handleDateField(finProdInHeadVO, "dmakedate", sdf, finProdInHead);

        //cprocalbodyoid（生产库存组织）
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "cprocalbodyoid");

        //billmaker（制单人）
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "billmaker");

        //cprowarehouseid（生产仓库）
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "cprowarehouseid");

        // creationtime（创建时间）
        DataProcessUtils.handleDateField(finProdInHeadVO, "creationtime", sdf, finProdInHead);

        // pk_org_v（库存组织）
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "pk_org_v");

        // dbilldate（单据日期）
        DataProcessUtils.handleDateField(finProdInHeadVO, "dbilldate", sdf, finProdInHead);

        //modifiedtime（最后修改时间）
        DataProcessUtils.handleDateField(finProdInHeadVO, "modifiedtime", sdf, finProdInHead);

        //creator（创建人）
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "creator");

        //cwarehouseid（仓库）
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "cwarehouseid");

        //vtrantypecode（出库类型编码）
        DataProcessUtils.getStringField(finProdInHeadVO, "vtrantypecode", finProdInHead);

        //vdef1(生产成本项目）
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "vdef1");

        //vdef19(理论产量)
        DataProcessUtils.getFormattedDouble(finProdInHeadVO, "vdef19", finProdInHead);

        // vnote（备注）
        DataProcessUtils.getStringField(finProdInHeadVO, "vnote", finProdInHead);
        return finProdInHead;
    }

    /**
     * 解析NCC产成品入库单行数据
     *
     * @param finProdInBodyVO 产品入库单行数据
     * @return 解析结果
     */
    private Map<String, Object> parsingFinProdInBodyVO(JSONObject finProdInBodyVO) {
        HashMap<String, Object> finProdInBody = new HashMap<>(20);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // dproducedate(生产日期）
        DataProcessUtils.handleDateField(finProdInBodyVO, "dproducedate", sdf, finProdInBody);

        //cbodytranstypecode（入库类型编码）
        DataProcessUtils.getStringField(finProdInBodyVO, "cbodytranstypecode", finProdInBody);

        // nshouldassistnum(应收数量）
        DataProcessUtils.getFormattedDouble(finProdInBodyVO, "nshouldassistnum", finProdInBody);

        // dbizdate（入库日期）
        DataProcessUtils.handleDateField(finProdInBodyVO, "dbizdate", sdf, finProdInBody);

        //cgeneralhid(表头主键）
        DataProcessUtils.processField(finProdInBodyVO, finProdInBody, "cgeneralhid");

        // cgeneralbid（表体主键）
        DataProcessUtils.getStringField(finProdInBodyVO, "cgeneralbid", finProdInBody);

        //cstateid（库存状态）
        DataProcessUtils.processField(finProdInBodyVO, finProdInBody, "cstateid");

        //pk_batchcode（批次主键）
        DataProcessUtils.processField(finProdInBodyVO, finProdInBody, "pk_batchcode");

        //cproductid（主产品）
        DataProcessUtils.processField(finProdInBodyVO, finProdInBody, "cproductid");

        // nshouldnum（应收主数量）
        DataProcessUtils.getFormattedDouble(finProdInBodyVO, "nshouldnum", finProdInBody);

        // castunitid（单位）
        DataProcessUtils.processField(finProdInBodyVO, finProdInBody, "castunitid");

        //cunitid（主单位）
        DataProcessUtils.processField(finProdInBodyVO, finProdInBody, "cunitid");

        //vapprovalnumber_148(批准文号）
        DataProcessUtils.getStringField(finProdInBodyVO, "vapprovalnumber_148", finProdInBody);

        //dvalidate（失效日期）
        DataProcessUtils.handleDateField(finProdInBodyVO, "dvalidate", sdf, finProdInBody);

        //fproductclass（产品类型）
        DataProcessUtils.getStringField(finProdInBodyVO, "fproductclass", finProdInBody);

        //cmaterialvid（物料编码）
        DataProcessUtils.processField(finProdInBodyVO, finProdInBody, "cmaterialvid");
        @SuppressWarnings("unchecked")
        HashMap<String, Object> pkMaterial = (HashMap<String, Object>) finProdInBody.get("cmaterialvid");

        // 查询物料的其他信息（保质期管理、批次管理、保质期单位、保质期）
        String pk = pkMaterial.get("pk").toString();
        HashMap<String, Object> additionMap = wareHouseInventoryMapper.selectAdditionalManagement(pk);
        finProdInBody.put("additionMap", additionMap);

        // cmaterialoid（物料编码最新版本）
        DataProcessUtils.processField(finProdInBodyVO, finProdInBody, "cmaterialoid");

        //vchangerate(换算率）
        DataProcessUtils.getStringField(finProdInBodyVO, "vchangerate", finProdInBody);

        //crowno（行号）
        DataProcessUtils.getStringField(finProdInBodyVO, "crowno", finProdInBody);

        // vlotno_148（产品批号）
        DataProcessUtils.getStringField(finProdInBodyVO, "vlotno_148", finProdInBody);

        // vbatchcode（批号）
        DataProcessUtils.getStringField(finProdInBodyVO, "vbatchcode", finProdInBody);

        // vbdef20（合箱说明）
        DataProcessUtils.getStringField(finProdInBodyVO, "vbdef20", finProdInBody);

        // cproductorid（生产商信息）
        DataProcessUtils.processField(finProdInBodyVO, finProdInBody, "cproductorid");

        // pk_holder_148（持有人/注册人）
        DataProcessUtils.processField(finProdInBodyVO, finProdInBody, "pk_holder_148");

        // nassistnum（实收数量）
        DataProcessUtils.getFormattedDouble(finProdInBodyVO, "nassistnum", finProdInBody);

        // vbdef48 （复检有效期）
        DataProcessUtils.getStringField(finProdInBodyVO, "vbdef48", finProdInBody);

        return finProdInBody;
    }
}
